/** console.d - Console classes

  Written in the D programming language 1.0
  This file is part of the Dotmars Framework.

Authors:	Wei Li (oldrev@gmail.com)
License:	BSD
Copyright:	Copyright (C) 2007 The Dotmars Team.
Date:		May 17 2007
*/

module dotmars.io.console;

import dotmars.platform.base;
import dotmars.text.utf;
import dotmars.text.encoding;
import dotmars.text.utf8;
import dotmars.text.utf16;

import dotmars.io.stream;
import dotmars.io.filestream;
import dotmars.io.accessor;
import dotmars.base.stdexcept;
import dotmars.base.convert;


private enum ConsoleType
{
	StdIn,
	StdOut,
	StdErr
}


private final class ConsoleStream : FileStream
{
	private ConsoleType m_type;

	public const char[1] EndOfLine = "\n";
//////// 平台无关代码 /////////////////////

	public void writeNewLine() {
		write(EndOfLine);
	}

	public void writeLine(char[] string)
	in {
		assert(string !is null);
	}
	body {
		write(string);
		writeNewLine();
	}

	public ConsoleType type() {
		return m_type;
	}

	public bool readable() {
		return m_type == ConsoleType.StdIn; 
	}

	public bool writable() {
		return (m_type == ConsoleType.StdOut) || (m_type == ConsoleType.StdErr);
	}

	public bool seekable() {
		return false;
	}

//////// 平台相关代码 ////////////////////////

version(Windows){

	public this(ConsoleType type)
	{
		FileAccess access;
		char[] filename;

		switch(type)
		{
			case ConsoleType.StdIn:
				filename = "CONIN$";
				access = FileAccess.Read;
				break;
			
			case ConsoleType.StdOut:
				filename = "CONOUT$";
				access = FileAccess.Write;
				break;

			case ConsoleType.StdErr:	
				filename = "CONOUT$";
				access = FileAccess.Write;
				break;
		}
		
		super(filename, FileMode.Open, access, FileShare.None);
		m_type = type;
	}

	public void write(char[] string)
	in {
		assert(writable);
		assert(string !is null);
	}
	body {
		wchar[] wstr = toUtf16(string);
		write(wstr);
	}

	public void write(wchar[] wstring)
	in {
		assert(writable);
		assert(wstring !is null);
	}
	body {
		DWORD written;
		WriteConsoleW(handle, wstring.ptr, wstring.length, &written, null);
		if(wstring.length != written)
			throw new IOException("Failed To write console");
	}

	public void write(dchar[] dstring)
	in {
		assert(writable);
		assert(dstring !is null);
	}
	body {
		wchar[] wstr = toUtf16(dstring);
		write(wstr);
	}

	/*
	public size_t read(char[] string, size_t count)
	{
		if(count > 64 * 1024)
			throw new ArgumentOutOfRangeException("Argument count too big");

		wchar[] 
		ReadConsoleW(handle, 

	}
	*/

	public override void flush()
	{
		if(m_type == ConsoleType.StdIn)
			FlushConsoleInputBuffer(handle);
		else
			FileStream.flush();
	}

}//version(Windows)


version(Posix){

	public this(ConsoleType type)
	{
		FileAccess access;
		char[] filename;

		switch(type)
		{
			case ConsoleType.StdIn:
				filename = "/dev/tty";
				access = FileAccess.Read;
				break;
			
			case ConsoleType.StdOut:
				filename = "/dev/tty";
				access = FileAccess.Write;
				break;

			case ConsoleType.StdErr:	
				filename = "/dev/tty";
				access = FileAccess.Write;
				break;
		}
		
		super(filename, FileMode.Open, access, FileShare.None);
		m_type = type;
	}

	public void write(char[] string)
	{
		FileStream.write(string.ptr, string.length);
	}

}//version(Posix)
}


////////////////////////////////////////////////////////////////////////////////

private final class ConsoleWriter : StreamWriter
{
	public this(ConsoleStream stream)
	in {
		assert(stream !is null);
	}
	body {
		super(stream, null); 
		newLine = ConsoleStream.EndOfLine;
	}

	protected override void writeString(char[] str)
	in {
		assert(str !is null);
	}
	body {
		getStream().write(str);
	}

	private ConsoleStream getStream() {
		ConsoleStream s = cast(ConsoleStream)m_stream;
		assert(s !is null);
		return s;
	}
}

////////////////////////////////////////////////////////////////////////////////


/** Represents the standard input, output, and error streams for console applications.
*/
final static class Console
{
	private static ConsoleStream m_stdin;  //TODO: ConsoleReader
	private static ConsoleWriter m_stdout;
	private static ConsoleWriter m_stderr;
	
	public static this()
	{
		m_stdin = new ConsoleStream(ConsoleType.StdIn); 
		m_stdout = new ConsoleWriter(new ConsoleStream(ConsoleType.StdOut)); 
		m_stderr = new ConsoleWriter(new ConsoleStream(ConsoleType.StdErr)); 
	}

	public static ~this()
	{
		m_stdin.flush();
		m_stdout.flush();
		m_stderr.flush();
	}

	public static synchronized ConsoleStream stdin()
	{
		return m_stdin;
	}

	public static synchronized TextWriter stdout()
	{
		return m_stdout;
	}

	public static synchronized TextWriter stderr()
	{
		return m_stderr;
	}

	public static synchronized void newLine()
	{
		m_stdout.write(m_stdout.newLine);
	}

	public static void write(T)(T value) 
	{
		synchronized {
			m_stdout.write!(T)(value);
		}
	}

	public static void writeLine(T)(T value) 
	{
		synchronized {
			m_stdout.writeLine(value);
		}
	}

	public static synchronized void print(char[] fmt, ...)
	{
		m_stdout.print(_argptr, _arguments, fmt);
	}

	public static synchronized void printLine(char[] fmt, ...)
	{
		m_stdout.print(_argptr, _arguments, fmt);
		m_stdout.write(m_stdout.newLine());
	}
}

